package com.bing.server;

import com.bing.common.MessageCodec;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.Scanner;

/**
 * 聊天服务器
 */
public class ChatServer {

    private static final Logger logger = LoggerFactory.getLogger(ChatServer.class);
    private int port;

    public ChatServer(int port) {
        this.port = port;
    }

    /**
     * 检查端口是否可用
     * 
     * @param port 要检查的端口
     * @return 如果端口可用返回true
     */
    private static boolean isPortAvailable(int port) {
        try (ServerSocket serverSocket = new ServerSocket()) {
            // 设置 SO_REUSEADDR 选项为 true，在服务器停止后能立即重用端口
            serverSocket.setReuseAddress(true);
            serverSocket.bind(new InetSocketAddress(port));
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 寻找可用端口
     * 
     * @param startPort 开始尝试的端口
     * @return 找到的可用端口
     */
    private static int findAvailablePort(int startPort) {
        int port = startPort;
        while (!isPortAvailable(port) && port < 65535) {
            port++;
        }
        return port < 65535 ? port : -1;
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 检查端口是否可用
            if (!isPortAvailable(port)) {
                System.out.println("端口 " + port + " 已被占用！");
                int newPort = findAvailablePort(port + 1);
                if (newPort > 0) {
                    System.out.println("自动切换到可用端口: " + newPort);
                    port = newPort;
                } else {
                    throw new RuntimeException("无法找到可用端口，请关闭占用端口的程序或指定其他端口");
                }
            }

            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(
                                    // 消息分割
                                    new LengthFieldBasedFrameDecoder(65535, 0, 4, 0, 4),
                                    new LengthFieldPrepender(4),

                                    // 编解码器
                                    new StringDecoder(CharsetUtil.UTF_8),
                                    new StringEncoder(CharsetUtil.UTF_8),
                                    new MessageCodec(),

                                    // 业务处理器
                                    new ServerHandler());
                        }
                    });

            System.out.println("聊天服务器启动，端口: " + port);

            // 绑定端口并启动
            ChannelFuture f = b.bind(port).sync();
            System.out.println("服务器启动成功！输入 'exit' 关闭服务器");

            // 启动控制台输入线程，用于关闭服务器
            new Thread(() -> {
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    String input = scanner.nextLine();
                    if ("exit".equalsIgnoreCase(input)) {
                        f.channel().close();
                        break;
                    }
                }
                scanner.close();
            }).start();

            // 等待服务器关闭
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            System.out.println("聊天服务器已关闭");
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 9999;

        // 检查命令行参数
        if (args.length > 0) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.out.println("端口参数格式错误，使用默认端口: " + port);
            }
        }

        // 提供交互式输入
        if (args.length == 0) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("==== 网络聊天室 ====");
            System.out.println("请选择启动模式:");
            System.out.println("1. 服务器模式");
            System.out.println("2. 客户端模式");
            System.out.print("请输入选择 (1/2): ");
            String choice = scanner.nextLine().trim();

            if (!"1".equals(choice)) {
                System.out.println("启动客户端模式...");
                try {
                    Class.forName("com.bing.client.ChatClient").getMethod("main", String[].class)
                            .invoke(null, (Object) new String[0]);
                } catch (Exception e) {
                    System.out.println("启动客户端失败: " + e.getMessage());
                }
                return;
            }

            System.out.print("请输入服务器端口 (默认9999): ");
            String portInput = scanner.nextLine().trim();
            if (!portInput.isEmpty()) {
                try {
                    port = Integer.parseInt(portInput);
                } catch (NumberFormatException e) {
                    System.out.println("端口格式错误，使用默认端口: " + port);
                }
            }

            System.out.println("启动服务器，端口: " + port);
        }

        // 启动服务器
        try {
            new ChatServer(port).start();
        } catch (Exception e) {
            System.err.println("服务器启动失败: " + e.getMessage());
            e.printStackTrace();

            // 延迟退出，让用户有时间看到错误信息
            Thread.sleep(5000);
        }
    }
}